import fs from 'fs';
import path from 'path';
import rimraf from 'rimraf';
import clone from './clone';

const resolve = (dir) => path.join(__dirname, '..', dir);
const capitalize = (str) => str.toLowerCase().replace(/\b(\w)|\s(\w)/g, (m) => m.toUpperCase());
const ensureSlash = (str) => (/\/$/g.test(str) ? str : str + '/');

const utils = {
	resolve: (dir) => {
		return resolve(dir);
	},

	normalizeSlash: (str) => {
		// Convert Windows backslash paths to slash paths
		let isExtendedLengthPath = /^\\\\\?\\/.test(str);
		let hasNonAscii = /[^\u0000-\u0080]+/.test(str);
		if (isExtendedLengthPath || hasNonAscii) {
			return str;
		}
		return str.replace(/\\/g, '/');
	},

	mkdir: (file) => {
		// Recursively create folder
		const root = resolve(file);
		let dirs = root.split(path.sep);
		let filepath;
		if (path.isAbsolute(root) === true) {
			filepath = dirs.shift();
			if (!filepath) filepath = path.sep;
		} else {
			filepath = path.resolve();
		}
		const mk = (dir, arr) => {
			const f = arr.shift();
			if (!f) return;
			const d = path.join(dir, f);
			if (fs.existsSync(d) === true) {
				return mk(d, arr);
			}
			const err = fs.mkdirSync(d, '0777');
			return err ? err : mk(d, arr);
		};
		return mk(filepath, dirs);
	},

	rmdir: (files, done, log) => {
		// rimraf folders recursively
		const rm = (files, done) => {
			if (files.length == 0) done();
			else {
				const f = files.pop();
				rimraf(f, () => {
					if (log) utils.log('Removed -', 36, f);
					rm(files, done);
				});
			}
		};
		rm(files, done);
	},

	isdir: (file) => {
		return fs.lstatSync(file).isDirectory();
	},

	getdir: (dir, rule) => {
		// e.g
		// fs.readdirSync( resolve('dist/') ).filter(file => (/\.worker\.js/gi).test(file) );
		return fs.readdirSync(resolve(dir)).filter((file) => RegExp(rule, 'gi').test(file));
	},

	grab: (dir, rule, done) => {
		// grab all files recursively
		// e.g
		// utils.grab('jsons/', '\.json', (err, results)=> {
		// 	if (err) throw err;
		// 	console.log(results);
		// });

		const walk = (directory, filterString, cb) => {
			let results = [];
			let i = 0;
			let list = null;
			const next = () => {
				let file = list[i++];
				if (!file) {
					if (filterString) results = results.filter((f) => RegExp(filterString, 'gi').test(f));
					return cb(null, results);
				}
				file = directory.replace(/\/$/g, '') + '/' + file;
				fs.stat(file, (err, stat) => {
					if (stat && stat.isDirectory()) {
						walk(file, filterString, (err, res) => {
							results = results.concat(res);
							next();
						});
					} else {
						if (!utils.isJunk(file)) {
							results.push(file);
						}
						next();
					}
				});
			};
			fs.readdir(directory, (err, res) => {
				if (err) return cb(err, null);
				list = res;
				next();
			});
		};

		walk(dir, rule, done);
	},

	log: (msg1, c1, msg2, c2, msg3) => {
		c1 = c1 != undefined ? c1 : 33;
		c2 = c2 != undefined ? c2 : 0;
		if (msg2 != undefined) {
			if (msg3 == undefined) {
				console.log('\x1b[' + c1.toString() + 'm%s\x1b[' + c2.toString() + 'm', msg1, msg2 + '\x1b[0m');
			} else {
				console.log('\x1b[' + c1.toString() + 'm%s\x1b[' + c2.toString() + 'm', msg1, msg2 + '\x1b[0m', msg3);
			}
		} else {
			console.log('\x1b[' + c1.toString() + 'm%s\x1b[0m', msg1);
		}
	},

	exist: (file) => {
		return fs.existsSync(file);
	},

	capitalize: (str) => {
		return capitalize(str);
	},

	ensureSlash: (str) => {
		return ensureSlash(str);
	},

	formatBytes: (bytes) => {
		// const stats = fs.statSync(file);
		// const bytes = stats.size;
		if (bytes <= 0) return 'N/A';
		const units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
		const exponent = Math.min(Math.floor(Math.log10(bytes) / 3), units.length - 1);
		bytes = Number((bytes / Math.pow(1000, exponent)).toPrecision(3));
		return bytes + units[exponent];
	},

	clone: (files, opts, done) => {
		// clone folders recursively
		const cl = (files, done) => {
			if (files.length == 0) done();
			else {
				const f = files.pop();
				clone(resolve(f.from), resolve(f.to), opts, (err) => {
					if (err) done(err);
					cl(files, done);
				});
			}
		};
		cl(files, done);
	},

	copy: (from, to) => {
		fs.createReadStream(resolve(from)).pipe(fs.createWriteStream(resolve(to)));
	},

	ext: (file) => {
		return path.extname(file).replace(/\./, '').toLowerCase();
	},

	isJunk: (str) => {
		return /(^\.)|(\.(DS_Store|log|Trashes|__MACOSX|db|ini)$)/gi.test(str);
	},

	hash: (len = 10) => {
		const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
		let text = possible.charAt(~~(Math.random() * 51));
		for (let i = 1; i < len; i++) text += possible.charAt(~~(Math.random() * possible.length));
		return text;
	},

	minifyJSON: (str) => {
		// ported from JSON.minify - Kyle Simpson
		let token = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g;
		let b1 = false; // in string
		let b2 = false; // in multiline comment
		let b3 = false; // in singleline comment
		let s1, s2, lc, rc;
		let i = 0,
			j = 0;
		let arr = [];

		token.lastIndex = 0;
		while ((s1 = token.exec(str))) {
			lc = RegExp.leftContext;
			rc = RegExp.rightContext;
			if (!b2 && !b3) {
				s2 = lc.substring(i);
				if (!b1) {
					s2 = s2.replace(/(\n|\r|\s)*/g, '');
				}
				arr[j++] = s2;
			}
			i = token.lastIndex;
			if (s1[0] == '"' && !b2 && !b3) {
				s2 = lc.match(/(\\)*$/);
				// start of string with ", or unescaped " character found to end string
				if (!b1 || !s2 || s2[0].length % 2 == 0) {
					b1 = !b1;
				}
				i--; // include " character in next catch
				rc = str.substring(i);
			} else if (s1[0] == '/*' && !b1 && !b2 && !b3) {
				b2 = true;
			} else if (s1[0] == '*/' && !b1 && b2 && !b3) {
				b2 = false;
			} else if (s1[0] == '//' && !b1 && !b2 && !b3) {
				b3 = true;
			} else if ((s1[0] == '\n' || s1[0] == '\r') && !b1 && !b2 && b3) {
				b3 = false;
			} else if (!b2 && !b3 && !/\n|\r|\s/.test(s1[0])) {
				arr[j++] = s1[0];
			}
		}
		arr[j++] = rc;
		return arr.join('');
	}
};

export default utils;
